/*
 *  SoundList.cpp
 *  SoundController0002
 *
 *  Created by jiro on 11/06/16.
 *  Copyright 2011 jiro music. All rights reserved.
 *
 */

#include "SoundList.h"
#include "Sound.h"
#include "HarmonicSystem.h"
#include "../../sc/AllExceptions.h"
#include <sstream>
#include <vector>

namespace sc
{
namespace hs
{

	SoundList::SoundList()
		: sounds(),
		  isDirty( true ), 
		  howlList()
	{}

	SoundList::~SoundList()
	{}
	
	void SoundList::push_back( const Sound& sound )
	{
		isDirty = true;
		sounds.push_back( sound );
	}
		
	void SoundList::clear()
	{
		isDirty = true;
		sounds.clear();
	}
		
	size_t SoundList::size() const
	{
		return sounds.size();
	}
	
	const Sound& SoundList::operator [] ( size_t index ) const
	{
		return sounds[ index ];
	}

	Sound& SoundList::operator [] ( size_t index )
	{
		return sounds[ index ];
	}
		
	const Sound& SoundList::at( size_t index ) const
	{
		return sounds.at( index );
	}

	Sound& SoundList::at( size_t index )
	{
		return sounds.at( index );
	}


	const HowlSet& SoundList::GetHowlSet()
	{
		if ( not isDirty )
		{
			return howlList;
		}

		isDirty = false;
		howlList.clear();
		for ( size_t i = 0; i < size(); i++ )
		{
			const Sound& soundI = sounds.at( i );
			for ( size_t j = i + 1; j < size(); j++ )
			{
				const Sound& soundJ = sounds.at( j );
				for ( int ii = 1; ii <= soundI.GetHarmonicsCount(); ii++ )
				{
					for ( int jj = 1; jj <= soundJ.GetHarmonicsCount(); jj++ ) 
					{
						double frequencyI = ii * WellTemperament::GetFrequency( soundI.GetMidiNumber() );
						double frequencyJ = jj * WellTemperament::GetFrequency( soundJ.GetMidiNumber() );
						if ( frequencyI < HarmonicSystem::LowestFrequency || HarmonicSystem::HighestFrequency < frequencyI ||
							 frequencyJ < HarmonicSystem::LowestFrequency || HarmonicSystem::HighestFrequency < frequencyJ )
						{
							continue;
						}

						double delta = fabs( frequencyI - frequencyJ );
						if ( ( delta == 0.0 ) || ( delta > HarmonicSystem::HowlFrequencyMax ) )
//						if ( ( delta > HarmonicSystem::HowlFrequencyMax ) )
						{
							if ( frequencyI < frequencyJ )
							{
								break;
							}
							continue;
						}
						howlList.insert(
							Howl(
								soundI.GetWaveType(), soundJ.GetWaveType(),
								soundI.GetMidiNumber(),  soundJ.GetMidiNumber(),
								ii, jj ) );
					}
				}
			}
		}
		return howlList;
	}

	const Sound::NearbyWellTemperamentNotes& SoundList::GetNearbyWellTemperamentNotes()
	{
		if ( not isDirty )
		{
			return nearbyWellTemeramentNotes;
		}

		nearbyWellTemeramentNotes.clear();
		for ( int i = 0; i < size(); i++ )
		{
			Sound& sound = at( i );
			const Sound::NearbyWellTemperamentNotes& nearbyNotes = sound.GetNearbyWellTemperamentNotes();
			Sound::NearbyWellTemperamentNotes::const_iterator iter = nearbyNotes.begin();
			for ( ; iter != nearbyNotes.end(); iter++ )
			{
				nearbyWellTemeramentNotes.insert( 
					std::pair< HarmonicSound, sc::Note::MidiNumber >( iter->first, iter->second ) );
			}
		}
		return nearbyWellTemeramentNotes;
	}


	bool SoundList::FindNoteWhereMidiNumber( sc::Note::MidiNumber number, std::vector< HarmonicSound >& sounds )
	{
		sounds.clear();
		const Sound::NearbyWellTemperamentNotes& nearNotes = GetNearbyWellTemperamentNotes();
		Sound::NearbyWellTemperamentNotes::const_iterator iter = nearNotes.begin();
		for ( ; iter != nearNotes.end(); iter++ )
		{
			if ( iter->second == number )
			{
				sounds.push_back( iter->first );
			}
		}

		if ( sounds.empty() )
		{
			return false;
		}

		return true;
	}

	bool SoundList::FindNotesIncludeHarmonics( double frequency, SoundList& soundList ) const
	{
		size_t count = soundList.size();
		for ( size_t i = 0; i < size(); i++ )
		{
			const Sound& sound = sounds.at( i );
			for ( int j = 1; j < sound.GetHarmonicsCount(); j++ )
//			for ( int j = 1; j < HarmonicSystem::HarmonicsCountMax; j++ )
			{
				if ( frequency == sound.GetHarmonics( j ) ) 
				{
					soundList.push_back( sound );
				}
			}
		}
		if ( soundList.size() == count ) {
			return false;
		}
		return true;
	}

	bool SoundList::FindNotesIncludeHarmonics( double fromFrequency, double toFrequency, SoundList& soundList ) const
	{
		size_t count = soundList.size();
		for ( size_t i = 0; i < size(); i++ )
		{
			const Sound& sound = sounds.at( i );
			for ( int j = 1; j < sound.GetHarmonicsCount(); j++ )
//			for ( int j = 1; j < HarmonicSystem::HarmonicsCountMax; j++ )
			{
				double harmonics = sound.GetHarmonics( j );
				if ( ( fromFrequency <= harmonics ) && ( harmonics <= toFrequency ) ) 
				{
					soundList.push_back( sound );
				}
			}
		}
		if ( soundList.size() == count ) {
			return false;
		}
		return true;
	}

	bool SoundList::IsExistNotesIncludeHarmonics( double frequency ) const
	{
		SoundList noteList;
		return FindNotesIncludeHarmonics( frequency, noteList );
	}

	bool SoundList::IsExistNotesIncludeHarmonics( double fromFrequency, double toFrequency ) const
	{
		SoundList noteList;
		return FindNotesIncludeHarmonics( fromFrequency, toFrequency, noteList );
	}
	
	bool SoundList::IsExistHowlFrequency( double howlFrequency ) const
	{
		//	calc all the harmonics of notes
		std::vector< std::vector< double > > harmonicsList;
		GetAllHarmonicsList( harmonicsList );

		//	make sure that howl is exist
		for ( int i = 0; i < harmonicsList.size(); i++ )
		{
			const std::vector< double >& harmonics0 = harmonicsList[ i ];
			for ( int j = i + 1; j < harmonicsList.size(); j++ )
			{
				const std::vector< double >& harmonics1 = harmonicsList[ j ];
				for ( int k0 = 0; k0 < harmonics0.size(); k0++ )
				{
					for ( int k1 = 0; k1 < harmonics1.size(); k1++ )
					{
						double frequency = fabs( harmonics0[ k0 ] - harmonics1[ k1 ] );
						if ( fabs( frequency - howlFrequency ) < WellTemperament::calculationError )
						{
							return true;
						}
					}
				} 				
			}
		}
		return false;
	}
	
	bool SoundList::FindHowlFrequency( double fromHowlFreqency, double toHowlFrequency, std::vector< double >& howlFrequencies ) const
	{
		//	calc all the harmonics of notes
		std::vector< std::vector< double > > harmonicsList;
		GetAllHarmonicsList( harmonicsList );

		//	make sure that howl is in the range
		bool isFound = false;
		for ( int i = 0; i < harmonicsList.size(); i++ )
		{
			const std::vector< double >& harmonics0 = harmonicsList[ i ];
			for ( int j = i + 1; j < harmonicsList.size(); j++ )
			{
				const std::vector< double >& harmonics1 = harmonicsList[ j ];
				for ( int k0 = 0; k0 < harmonics0.size(); k0++ )
				{
					for ( int k1 = 0; k1 < harmonics1.size(); k1++ )
					{
						double frequency = fabs( harmonics0[ k0 ] - harmonics1[ k1 ] );
						if ( fromHowlFreqency <= frequency && frequency <= toHowlFrequency )
						{
							howlFrequencies.push_back( frequency );
							isFound = true;
						}
					}
				} 				
			}
		}
		return isFound;
	}
	
	bool SoundList::FindTwoSoundsGeneratingHowlFreuency( double howlFrequency, Sound::NearbyWellTemperamentNotes& harmonicSounds ) const
	{
		throw NotImplementedException( "bool SoundList::FindTwoSoundsGeneratingHowlFreuency( double howlFrequency, std::multimap< HarmonicSound, HarmonicSound >& harmonicSounds ) const" );
	/*
		//	constance 
		const Wave::Type type = at( 0 ).GetWaveType();
		
		//	calc all the harmonics of notes
		std::vector< std::vector< double > > harmonicsList;
		GetAllHarmonicsList( harmonicsList );
	
		//	find that 2 sounds generating the howl
		size_t count = harmonicSounds.size();
		for ( int i = 0; i < harmonicsList.size(); i++ )
		{
			const std::vector< double >& harmonics0 = harmonicsList[ i ];
			for ( int j = i + 1; j < harmonicsList.size(); j++ )
			{
				const std::vector< double >& harmonics1 = harmonicsList[ j ];
				for ( int k0 = 0; k0 < harmonics0.size(); k0++ )
				{
					for ( int k1 = 0; k1 < harmonics1.size(); k1++ )
					{
						double frequency = fabs( harmonics0[ k0 ] - harmonics1[ k1 ] );
						if ( fabs( howlFrequency - frequency ) <= WellTemperament::calculationError )
						{
							harmonicSounds.insert( 
								TwoSounds(
									HarmonicSound( type, harmonics0[ 0 ], k0 + 1 ),
									HarmonicSound( type, harmonics1[ 0 ], k1 + 1 ) ) );
						}
					}
				} 				
			}
		}
		if ( harmonicSounds.size() == count )
		{
			return false;
		}
		return true;
		*/
	}

	void SoundList::GetAllHarmonicsList( std::vector< std::vector< double > >& harmonicsList ) const
	{
		//	calc all the harmonics of notes
		for ( size_t i = 0; i < size(); i++ )
		{
			const Sound& sound = sounds.at( i );
			std::vector< double > harmonics;
			sound.GetAllHarmonics( harmonics );
			harmonicsList.push_back( harmonics );
		}
	}

	std::string SoundList::ToString( int tabCount ) const
	{
		std::stringstream ss;
		std::string tabs;
		for ( int i = 0; i < tabCount; i++ )
		{
			tabs.append( "\t" );
		}
		ss << tabs << "SoundList:\n";
		for ( size_t i = 0; i < sounds.size(); i++ )
		{
			ss << sounds[ i ].ToString( tabCount + 1 );
			if ( i != sounds.size() - 1 )
			{
				ss << std::endl;
			} 
		}
		return ss.str();
	}

}
}
